package com.gzist.elderlyserviceplatform.service;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.gzist.elderlyserviceplatform.entity.po.*;
import io.milvus.client.MilvusClient;
import io.milvus.grpc.FlushRequest;
import io.milvus.param.collection.FlushParam;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.document.Document;
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
import org.springframework.ai.vectorstore.*;
import org.springframework.ai.vectorstore.filter.Filter;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.core.annotation.Order;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * MilvusService
 *
 * @description: Milvus知识库业务类
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class MilvusService implements ApplicationRunner {

    private final VectorStore vectorStore;
    private final NursingHomeService nursingHomeService;
    private final InfoNewsService infoNewsService;
    private final ElderlyServiceService elderlyServiceService;
    private final ActivityService activityService;
    private final VideoService videoService;
    private boolean initialSyncCompleted = false;

    /**
     * 定时执行Milvus知识库全量同步任务
     */
    @Scheduled(cron = "0 0 0 * * *") // 每天凌晨0点执行
    @Transactional
    public void scheduledSync() {
        log.info("开始定时Milvus知识库同步，时间：{}", LocalDateTime.now());
        deleteAll();
        executeFullSync();
    }

    // ================ 启动时同步逻辑 ================
    @Override
    @Order(0) // 确保在Flyway和Redis之后执行
    public void run(ApplicationArguments args) {
        if (!initialSyncCompleted) {
            log.info("应用启动后首次Milvus知识库同步开始");
            deleteAll();
            executeFullSync();
            initialSyncCompleted = true;
        }
    }

    /**
     * 执行全面同步操作
     * 该方法将调用多个同步方法以更新系统中的不同数据类型
     * 它使用了@Transactional注解，这意味着该方法内的所有操作都将在一个事务中执行
     * 如果过程中发生异常，事务将被回滚，以保持数据的一致性
     */
    @Transactional
    void executeFullSync() {
        try {
            syncNursingHomes();
            syncInfoNews();
            syncElderlyServices();
            syncActivities();
            syncVideos();
            log.info("Milvus知识库同步完成，时间：{}", LocalDateTime.now());
        } catch (Exception e) {
            log.error("同步过程中发生错误", e);
        }
    }

    /**
     * 同步养老院信息到向量数据库
     */
    private void syncNursingHomes() {
        List<NursingHome> homes = nursingHomeService.list(new QueryWrapper<NursingHome>().eq("del_flag", "0"));

        List<Document> docs = homes.stream()
                .map(home -> {
                    String content = String.format(
                            "养老院名称：%s，地址：%s，价格：%s，介绍：%s，标签：%s，封面图片：%s。",
                            home.getName(),
                            home.getAddress(),
                            home.getPrice(),
                            home.getContent(),
                            home.getTags(),
                            home.getImage()
                    );
                    Map<String, Object> metadata = new HashMap<>();
                    metadata.put("type", "养老院");
                    metadata.put("养老院名称", home.getName());
                    metadata.put("封面图片链接", home.getImage());
                    metadata.put("养老院价格", home.getPrice());
                    metadata.put("养老院地址", home.getAddress());
                    metadata.put("养老院标签", home.getTags());
                    // text为实际存储的向量数据，metadata为检索匹配条件
                    // 简单来说对于一个问题，先匹配metadata，然后匹配成功即检索到的数据便包含在text中
                    return Document.builder()
                            .id(home.getId())
                            .metadata(metadata)
                            .text(content)
                            .build();
                })
                .collect(Collectors.toList());
        vectorStore.add(docs);
        log.info("养老院信息同步完成，共{}条", docs.size());
    }

    /**
     * 同步资讯信息信息到向量数据库
     */
    private void syncInfoNews() {
        List<InfoNews> newsList = infoNewsService.list(new QueryWrapper<InfoNews>().eq("del_flag", "0"));

        List<Document> docs = newsList.stream()
                .map(news -> {
                    String content = String.format(
                            "标题：%s，简介：%s，内容：%s，封面图片：%s，发布时间：%s，浏览次数：%s。",
                            news.getTitle(),
                            news.getSummary(),
                            news.getContent(),
                            news.getCover(),
                            news.getPublishTime(),
                            news.getViewCount()
                    );
                    Map<String, Object> metadata = new HashMap<>();
                    metadata.put("type", "资讯信息");
                    metadata.put("资讯标题", news.getTitle());
                    metadata.put("资讯封面", news.getCover());
                    metadata.put("资讯发布时间", news.getPublishTime().toString());
                    metadata.put("资讯浏览次数", news.getViewCount());
                    return Document.builder()
                            .id(news.getId())
                            .metadata(metadata)
                            .text(content)
                            .build();
                })
                .collect(Collectors.toList());
        vectorStore.add(docs);
        log.info("资讯信息同步完成，共{}条", docs.size());
    }

    /**
     * 同步养老服务信息到向量数据库
     */
    private void syncElderlyServices() {
        List<ElderlyService> services = elderlyServiceService.list(new QueryWrapper<ElderlyService>().eq("del_flag", "0"));

        List<Document> docs = services.stream()
                .map(service -> {
                    String content = String.format(
                            "服务名称：%s，类型：%s，描述：%s，服务封面图片：%s。",
                            service.getName(),
                            service.getServiceType(),
                            service.getDescription(),
                            service.getCover()
                    );
                    Map<String, Object> metadata = new HashMap<>();
                    metadata.put("type", "养老活动");
                    metadata.put("养老服务名称", service.getName());
                    metadata.put("服务封面", service.getCover());
                    metadata.put("养老服务类型", service.getServiceType());
                    metadata.put("服务描述", service.getDescription());
                    return Document.builder()
                            .id(service.getId())
                            .metadata(metadata)
                            .text(content)
                            .build();
                })
                .collect(Collectors.toList());
        vectorStore.add(docs);
        log.info("养老服务同步完成，共{}条", docs.size());
    }

    /**
     * 同步活动信息到向量数据库
     */
    private void syncActivities() {
        List<Activity> activities = activityService.list(new QueryWrapper<Activity>().eq("del_flag", "0"));

        List<Document> docs = activities.stream()
                .map(activity -> {
                    String content = String.format(
                            "活动名称：%s，时间：%s至%s，地址：%s，活动内容：%s，活动封面图片：%s。",
                            activity.getName(),
                            activity.getStartDate(),
                            activity.getEndDate(),
                            activity.getAddress(),
                            activity.getContent(),
                            activity.getCover()
                    );
                    Map<String, Object> metadata = new HashMap<>();
                    metadata.put("type", "养老活动");
                    metadata.put("活动开始时间", activity.getStartDate().toString());
                    metadata.put("活动结束时间", activity.getEndDate().toString());
                    metadata.put("活动举行地点", activity.getAddress());
                    metadata.put("活动名称", activity.getName());
                    metadata.put("活动封面", activity.getCover());
                    return Document.builder()
                            .id(activity.getId())
                            .metadata(metadata)
                            .text(content)
                            .build();
                })
                .collect(Collectors.toList());
        vectorStore.add(docs);
        log.info("活动信息同步完成，共{}条", docs.size());
    }

    /**
     * 同步视频信息到向量数据库
     */
    private void syncVideos() {
        List<Video> videos = videoService.list(new QueryWrapper<Video>().eq("del_flag", "0"));

        List<Document> docs = videos.stream()
                .map(video -> {
                    String content = String.format(
                            "视频标题：%s，作者：%s，描述：%s，适合年龄：%s-%s岁，视频封面图片：%s。",
                            video.getTitle(),
                            video.getCreateBy(),
                            video.getDescription(),
                            video.getMinAge(),
                            video.getMaxAge(),
                            video.getCoverImage()
                    );
                    Map<String, Object> metadata = new HashMap<>();
                    metadata.put("type", "视频");
                    metadata.put("视频发布作者", video.getCreateBy());
                    metadata.put("视频适合年龄", String.format("%s-%s", video.getMinAge(), video.getMaxAge()));
                    metadata.put("视频封面", video.getCoverImage());
                    return Document.builder()
                            .id(video.getId())
                            .metadata(metadata)
                            .text(content)
                            .build();
                })
                .collect(Collectors.toList());
        vectorStore.add(docs);
        log.info("视频信息同步完成，共{}条", docs.size());
    }

    /**
     * 根据过滤条件删除数据接口
     */
    public void deleteByFilter(Filter.Expression expression) {
        Assert.notNull(expression, "过滤条件不能为空");
        vectorStore.delete(expression);
        log.info("根据条件删除数据：{}", expression);
    }

    /**
     * 实现全库清空逻辑
     */
    public void deleteAll() {
        int batchSize = 1000; // 根据数据量调整批次大小
        while (true) {
            SearchRequest request = SearchRequest.builder()
                    .query("1 == 1")
                    .similarityThresholdAll()
                    .topK(batchSize)
                    .build();
            List<Document> batch = vectorStore.similaritySearch(request);
            if (CollectionUtil.isEmpty(batch)) break;

            List<String> ids = batch.stream().map(Document::getId).collect(Collectors.toList());
            vectorStore.delete(ids);
        }
    }


    // ================ 文档分块处理 ================
    // 示例：分块处理长文本（如养老院介绍）
    private List<Document> splitDocument(String text, String idPrefix) {
        return new TokenTextSplitter(400, 100, 20, 200, true)
                .apply(List.of(Document.builder().text(text).build()))
                .stream()
                .map(doc -> doc.mutate()
                        .id(idPrefix + "_" + UUID.randomUUID())
                        .build())
                .collect(Collectors.toList());
    }
}